package seatiger.tomcat.util.http.mapper;

/**
 * 请求映射URI映射器，提供了映射规则，可以根据请求路径，获取到相应的servlet
 */
public final class Mapper {

    /**
     * 容器中的host
     */
    protected Host[] hosts = new Host[0];

    /**
     * 基础模型映射，name容器的名称，object 为具体的容器
     */
    protected static abstract class MapElement {
        public String name;
        public Object object;
    }

    /**
     * Wrapper映射模型
     */
    protected static class Wrapper extends MapElement {
        public String path = null;
    }

    /**
     * Context 映射模型
     */
    protected static class Context extends MapElement {
        public String path;
        public Wrapper defaultWrapper;
        //精确的匹配
        public Wrapper[] exactWrappers = new Wrapper[0];
        //通配符匹配 /*
        public Wrapper[] wildcardWrappers = new Wrapper[0];
        // *. 匹配
        public Wrapper[] extensionWrappers = new Wrapper[0];
    }

    /**
     * Host 映射模型
     */
    protected static class Host extends MapElement {
        public Context[] contexts = new Context[0];
    }

    /**
     * 通过二分查找 在map数组中通过name查找MapElement
     * 返回的是数组中位置
     *
     * @param map
     * @param name
     * @return
     */
    private static int find(MapElement[] map, String name) {
        int a = 0;
        int b = map.length - 1;
        if (b == -1) {
            return -1;
        }
        if (name.compareTo(map[0].name) < 0) {
            return -1;
        }
        if (b == 0) {
            return 0;
        }
        int i = 0;
        while (true) {
            i = (b + a) / 2;
            int result = name.compareTo(map[i].name);
            if (result > 0) {
                a = i;
            } else if (result == 0) {
                return i;
            } else {
                b = i;
            }
            if ((b - a) == 1) {
                int result2 = name.compareTo(map[b].name);
                if (result2 < 0) {
                    return a;
                } else {
                    return b;
                }
            }
        }
    }

    /**
     * 插入方法
     *
     * @param oldMap
     * @param newMap
     * @param newElement
     * @return
     */
    private static boolean insertMap(MapElement[] oldMap, MapElement[] newMap, MapElement newElement) {
        int pos = find(oldMap, newElement.name);
        //已经存在了
        if (pos != -1 && oldMap[pos].name.equals(newElement.name)) {
            return false;
        }
        System.arraycopy(oldMap, 0, newMap, 0, pos + 1);
        newMap[pos + 1] = newElement;
        System.arraycopy
                (oldMap, pos + 1, newMap, pos + 2, oldMap.length - pos - 1);
        return true;
    }

    /**
     * 映射host对象
     *
     * @param name
     * @param host
     */
    public synchronized void addHost(String name, Object host) {
        Host[] newHosts = new Host[hosts.length + 1];
        Host newHost = new Host();
        newHost.name = name;
        newHost.object = host;
        if (insertMap(hosts, newHosts, newHost)) {
            hosts = newHosts;
        }
    }

    /**
     * 添加context
     *
     * @param hostName
     * @param path
     * @param context
     */
    public void addContext(String hostName, String path, Object context) {
        Host[] hosts = this.hosts;
        int pos = find(hosts, hostName);
        if (pos < 0) {
            addHost(hostName, "");
            hosts = this.hosts;
            pos = find(hosts, hostName);
        }
        Host host = hosts[pos];
        if (host.name.equals(hostName)) {
            synchronized (host) {
                Context[] contexts = host.contexts;
                Context[] newContexts = new Context[contexts.length + 1];
                Context newContext = new Context();
                newContext.name = path;
                newContext.object = context;
                if (insertMap(contexts, newContexts, newContext)) {
                    host.contexts = newContexts;
                }
            }
        }
    }

    /**
     * 添加Wrapper
     * @param path
     * @param wrapper
     */
    protected void addWrapper(Context context, String path, Object wrapper) {
        synchronized (context) {
            Wrapper newWrapper = new Wrapper();
            newWrapper.object = wrapper;
            if (path.endsWith("/*")) {
                newWrapper.name = path.substring(0, path.length() - 2);
                Wrapper[] oldWrappers = context.wildcardWrappers;
                Wrapper[] newWrappers = new Wrapper[oldWrappers.length + 1];
                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                    context.wildcardWrappers = newWrappers;
                }
            } else if (path.startsWith("*.")) {
                newWrapper.name = path.substring(2);
                Wrapper[] oldWrappers = context.extensionWrappers;
                Wrapper[] newWrappers =
                        new Wrapper[oldWrappers.length + 1];
                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                    context.extensionWrappers = newWrappers;
                }
            } else if (path.equals("/")) {
                newWrapper.name = "";
                context.defaultWrapper = newWrapper;
            } else {
                //精准匹配
                newWrapper.name = path;
                Wrapper[] oldWrappers = context.exactWrappers;
                Wrapper[] newWrappers =
                        new Wrapper[oldWrappers.length + 1];
                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                    context.exactWrappers = newWrappers;
                }
            }
        }
    }

    /**
     * 添加Wrapper
     * @param hostName
     * @param contextPath
     * @param path
     * @param wrapper
     */
    public void addWrapper(String hostName, String contextPath, String path, Object wrapper) {
        Host[] hosts = this.hosts;
        int pos = find(hosts, hostName);
        if (pos < 0) {
            return;
        }
        Host host = hosts[pos];
        if (host.name.equals(hostName)) {
            Context[] contexts = host.contexts;
            int pos2 = find(contexts, contextPath);
            Context context = contexts[pos2];
            if (context.name.equals(contextPath)) {
                addWrapper(context, path, wrapper);
            }
        }
    }

    /**
     *  查找 seatiger.tomcat.catalina.Wrapper
     * @param hostName
     * @param uri
     * @return seatiger.tomcat.catalina.Wrapper
     */
    public Object findWrapper(String hostName,String uri) {
        Host host = null;
        for (Host h : hosts) {
            if (h.name.equals(hostName)) {
                host = h;
                break;
            }
        }
        if (host == null) {
            return null;
        }
        Context context = null;
        String path = null;
        for (Context c : host.contexts) {
            if (uri.startsWith(c.name)) {
                int length = c.name.length();
                path = uri.substring(length);
                context = c;
                break;
            }
        }
        if (context == null) {
            return null;
        }
        Wrapper wrapper = findWrapper(context.exactWrappers,path);
        wrapper = wrapper == null ? findWrapper(context.wildcardWrappers,path) : wrapper;
        wrapper = wrapper == null ? findWrapper(context.extensionWrappers,path) : wrapper;
        return wrapper == null ? null : wrapper.object;
    }

    private Wrapper findWrapper(Wrapper[] wrappers,String path) {
        if (wrappers != null && path != null) {
            for (Wrapper w1 : wrappers) {
                if (w1.name.equals(path)){
                    return w1;
                }
            }
        }
        return null;
    }
}
